//
// Created by xzl on 2018/2/27.
//

#ifndef FTPDOWNLOADER_MVMANAGER_H
#define FTPDOWNLOADER_MVMANAGER_H

#include <mutex>
#include <string>
#include <set>
#include <unordered_map>
#include "sqlite_orm.h"
#include "FtpUpdateor.h"
#include "Poller/Timer.h"
#include "Util/TimeTicker.h"
#include "Util/function_traits.h"
#include "Util/ResourcePool.h"
#include "Common/config.h"
#include "Util/util.h"
#include "Thread/ThreadPool.h"

using namespace std;
using namespace sqlite_orm;
using namespace ZL::Util;
using namespace ZL::Poller;
using namespace ZL::Thread;

#define MAKE_COLUMN(cls, field, ...)  make_column(#field, &cls::field)
#define CHECK_THREAD() do{if(_threadid != this_thread::get_id()){WarnL << "不得在其他线程调用该函数！";}}while(0);
//记录已下载文件
struct MvInfoMoudle {
    ///////vod.db已有信息//////////
    //歌曲编码，唯一键
    string songbm;
    //文件名,生成url用
    string filename;

    ////////本地信息//////////
    uint64_t row_id = 0;
    //下载url
    string down_url;
    //文件路径
    string file_path;
    //更新时间
    uint64_t update_time = 0;
    //创建时间
    uint64_t create_time = 0;


    //是否下载完成
    int completed = 0;

    //下载失败次数
    int failed_count = 0;
    //下载失败代码
    int failed_code = 0;
    //下载失败msg
    string failed_msg;
    //被当前进程锁定
    uint64_t lock_id = 0;

};


namespace Ftp {
    extern const char kDbFileRemote[];// = FTP_FIELD"db_file_remote";
    extern const char kDbFileLocal[];// = FTP_FIELD"db_file_local";
}//namespace Ftp

class MvManager {
public:
    static MvManager &Instance() {
        static MvManager instance;
        return instance;
    }

    static void Destory() {
        Instance().stop();
    }

private:
    MvManager();
    ~MvManager();
    void stop();

    //获取远程数据库实例
    auto &getRemoteStorage() {
        static auto onCreatStorage = [](const string &file) {
            WarnL << "getRemoteStorage:reopen db file:" << file;
            auto storagePtr = new_storage(file,
                                        make_table("songs",
                                                   MAKE_COLUMN(MvInfoMoudle, songbm),
                                                   MAKE_COLUMN(MvInfoMoudle, filename)
                                        )
            );
            storagePtr->open_forever();
            return storagePtr;
        };
        using storagePtrType = function_traits<decltype(onCreatStorage)>::return_type;
        if (!_remoteDb) {
            GET_CONFIG_AND_REGISTER(string, ftp_remote_db_file, Ftp::kDbFileRemote);
            auto storagePtr = onCreatStorage(ftp_remote_db_file);
            _remoteDb.reset((void *)storagePtr, [](void *ptr) {
                delete (storagePtrType) ptr;
                WarnL << "release remote database";
            });
        }
        CHECK_THREAD();
        return *((storagePtrType) _remoteDb.get());
    }

    //获取本地数据库实例
    auto &getLocalStorage() {
        static auto onCreatStorage = [](const string &file) {
            WarnL << "getLocalStorage:reopen db file:" << file;
            auto storagePtr = new_storage(file,
                                        make_unique_index("idx_MvInfoMoudle_songbm", &MvInfoMoudle::songbm),
                                        make_table("MvInfoMoudle",//正在下载的文件记录表
                                                   MAKE_COLUMN(MvInfoMoudle, songbm),
                                                   MAKE_COLUMN(MvInfoMoudle, filename),
                                                   make_column("row_id", &MvInfoMoudle::row_id, autoincrement(),primary_key()),
                                                   MAKE_COLUMN(MvInfoMoudle, down_url),
                                                   MAKE_COLUMN(MvInfoMoudle, file_path),
                                                   MAKE_COLUMN(MvInfoMoudle, update_time),
                                                   MAKE_COLUMN(MvInfoMoudle, create_time),
                                                   MAKE_COLUMN(MvInfoMoudle, completed),
                                                   MAKE_COLUMN(MvInfoMoudle, failed_count),
                                                   MAKE_COLUMN(MvInfoMoudle, failed_code),
                                                   MAKE_COLUMN(MvInfoMoudle, failed_msg),
                                                   MAKE_COLUMN(MvInfoMoudle, lock_id)

                                        )
            );
            storagePtr->open_forever();
            storagePtr->sync_schema();
            return storagePtr;
        };
        using storagePtrType = function_traits<decltype(onCreatStorage)>::return_type;
        if (!_localDb) {
            GET_CONFIG_AND_REGISTER(string, ftp_local_db_file, Ftp::kDbFileLocal);
            auto storagePtr = onCreatStorage(ftp_local_db_file);
            _localDb.reset((void *)(storagePtr), [](void *ptr) {
                delete (storagePtrType *) ptr;
                WarnL << "release local database";
            });
        }
        CHECK_THREAD();
        return *((storagePtrType) _localDb.get());
    }


#define queryLocalDB(...) \
    for(int i=0;i<3;++i){\
        try{\
            getLocalStorage().__VA_ARGS__;\
            break;\
        }catch(std::exception &ex){\
            _localDb.reset();\
            WarnL << ex.what();\
        }\
    }

#define queryLocalDBWithResult(ret, ...) \
     for(int i=0;i<3;++i){\
        try{\
            ret = getLocalStorage().__VA_ARGS__;\
            break;\
        }catch(std::exception &ex){\
            _localDb.reset();\
            WarnL << ex.what();\
        }\
    }

    //获取ftp下载器
    FtpUpdateor::Ptr getDownloader(const string &url);

    //取消下载
    void cancelDownloader(const string &url);

    //生成ftp绝对路径
    string getAbsolutelyUrl(const string &relative_url, const string &middle_dir = "");

    //获取保存路径
    string getSavedPath(const string &filename);

    //尝试更新数据库
    void checkUpdateRemoteDB();

    //定时管理
    bool onManager(bool now);

    //数据库更新结果
    void onUpdateDBResult(int code, const string &msg);

    //开始下载
    void startAllTasks();

    //恢复下载文件
    void resumTask(const MvInfoMoudle &moudle);

    //新建下载任务
    void createTasks(int size);

    //创建任务
    void createTask(const MvInfoMoudle &moudle, bool update);

    //获取最大的歌曲编号
    string getMaxLocalSongBM();

    typedef function<void(const string &full_url, int code, const string &msg)> onGuessUrl;

    //猜测完整的url路径
    void startGuessFullUrl(const string &fileName, const onGuessUrl &callback);

    void guessFullUrl(const string &fileName,
                      int disk_index,
                      int start_index,
                      int end_index,
                      const onGuessUrl &callback);


private:
    std::shared_ptr<void> _remoteDb;
    std::shared_ptr<void> _localDb;
    std::shared_ptr<Timer> _timer;
    unordered_map<string ,FtpUpdateor::Ptr> _downloaderMap;
    Ticker _tickerCheckDB;
    Ticker _tickerCheckTasks;
    bool _updatingDB = false;
    uint64_t _lock_id;

    //上次可用的硬盘名称
    int _success_disk;
    int _min_disk;
    int _max_disk;
    int _failed_disk;
    string _disk_prefix;
    ThreadPool _threadpool;
    thread::id _threadid;
};


#endif //FTPDOWNLOADER_MVMANAGER_H
